package cool.taomu.toolkit.service.centre.kademlia.service

import com.google.common.base.Optional
import com.google.common.util.concurrent.FutureCallback
import com.google.common.util.concurrent.ListeningExecutorService
import com.google.inject.Inject
import com.google.inject.name.Named
import cool.taomu.toolkit.executor.TaomuExecutors
import cool.taomu.toolkit.service.centre.kademlia.utils.PeerConfigureFileDownload
import cool.taomu.toolkit.service.kademlia.entity.FindNodeEntity
import cool.taomu.toolkit.service.kademlia.entity.KPeerNode
import cool.taomu.toolkit.service.kademlia.service.KRoutingTable
import java.util.List
import java.util.concurrent.Callable
import java.util.function.Function
import java.util.stream.Collectors
import org.apache.commons.lang3.exception.ExceptionUtils
import org.slf4j.LoggerFactory

/**
 * 节点查找
 */
class FindNodeService {
    val static LOG = LoggerFactory.getLogger(FindNodeService);

    @Inject
    KRoutingTable router;

    @Inject
    ListeningExecutorService executor;

    @Inject
    PeerConfigureFileDownload download

    @Inject
    @Named("taomu.toolkit.kademlia.findNode")
    Function<FindNodeEntity, List<KPeerNode>> findNode

    def Optional<List<KPeerNode>> findNode(String nodeId, List<String> excludeIds) {
        LOG.info("****findNode****:{}", nodeId);
        var findNodes = router.findClosestNodes(nodeId);
        LOG.info("查找到的节点数量:{}", findNodes.size);
        LOG.info("查找的id:{}", nodeId);
        val findNodeEntityList = <Callable<List<KPeerNode>>>newArrayList();
        excludeIds.add(router.localNode.id)
        findNodes.filter [
            !excludeIds.contains(it.id)
        ].forEach [ node |
            findNodeEntityList.add([
                LOG.info("连接远程服务器 {} 查找节点", node);
                return findNode.apply(new FindNodeEntity(nodeId, node, router.localNode, excludeIds))
            ])
        ]
        val List<KPeerNode> resultNodes = newArrayList();
        resultNodes.addAll(findNodes)
        var executors = TaomuExecutors.getInstance(this.executor)
        executors.tasks(findNodeEntityList).allAsList.callback(new FutureCallback<List<List<KPeerNode>>>() {

            override onFailure(Throwable t) {
                LOG.error(ExceptionUtils.getStackTrace(t))
            }

            override onSuccess(List<List<KPeerNode>> results) {
                var closestNodes = results.stream().filter[it !== null] // 过滤掉 null 的 List
                .flatMap[it.stream()] // 安全 flatMap
                .filter[it !== null] // 过滤掉可能为 null 的 KPeerNode
                .distinct.collect(Collectors.toList())
                LOG.info("closestNodes 返回节点数:{}", closestNodes.size);
                // resultNodes.addAll(closestNodes);
                resultNodes.addAll(closestNodes)
            }
        })
        if (resultNodes.size == 0) {
            return Optional.absent();
        }
        LOG.info("需要将网络上找到的{}个节点最新文件下载到本地并加到桶中", resultNodes.size);
        resultNodes.forEach [
            executor.execute([
                // 将找到的节点配置下载下来
                download.downlaodTorrent(it)
            ])
        ]
        var temp = resultNodes.toSet.toList
        resultNodes.clear
        resultNodes.addAll(temp);
        LOG.info("resultNodes 中的节点数 {} 排序找到的节点", resultNodes.size);
        resultNodes.sort([ n1, n2 |
            router.xorDistance(nodeId, n1.getId()).compareTo(router.xorDistance(nodeId, n2.getId()))
        ])
        var nodes = resultNodes.subList(0, Math.min(router.k, resultNodes.size()));
        return Optional.fromNullable(nodes)
    }
}
